//-----------------------------------------------------------------------------
//
// (c) Copyright 2009 Xilinx, Inc. All rights reserved.
//
// This file contains confidential and proprietary information
// of Xilinx, Inc. and is protected under U.S. and
// international copyright and other intellectual property
// laws.
//
// DISCLAIMER
// This disclaimer is not a license and does not grant any
// rights to the materials distributed herewith. Except as
// otherwise provided in a valid license issued to you by
// Xilinx, and to the maximum extent permitted by applicable
// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
// (2) Xilinx shall not be liable (whether in contract or tort,
// including negligence, or under any other theory of
// liability) for any loss or damage of any kind or nature
// related to, arising under or in connection with these
// materials, including for any direct, or any indirect,
// special, incidental, or consequential loss or damage
// (including loss of data, profits, goodwill, or any type of
// loss or damage suffered as a result of any action brought
// by a third party) even if such damage or loss was
// reasonably foreseeable or Xilinx had been advised of the
// possibility of the same.
//
// CRITICAL APPLICATIONS
// Xilinx products are not designed or intended to be fail-
// safe, or for use in any application requiring fail-safe
// performance, such as life-support or safety devices or
// systems, Class III medical devices, nuclear facilities,
// applications related to the deployment of airbags, or any
// other applications that could lead to death, personal
// injury, or severe property or environmental damage
// (individually and collectively, "Critical
// Applications"). Customer assumes the sole risk and
// liability of any use of Xilinx products in Critical
// Applications, subject only to applicable laws and
// regulations governing limitations on product liability.
//
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
// PART OF THIS FILE AT ALL TIMES.
//
//-----------------------------------------------------------------------------
// Project    : Spartan-6 Integrated Block for PCI Express
// File       : gtx_rx_valid_filter_v6.v
//-----------------------------------------------------------------------------

`timescale 1ns / 1ns

module GTX_RX_VALID_FILTER_V6 #(

  parameter           CLK_COR_MIN_LAT    = 28

)
(
  output  [1:0]       USER_RXCHARISK,
  output  [15:0]      USER_RXDATA,
  output              USER_RXVALID,
  output              USER_RXELECIDLE,
  output  [ 2:0]      USER_RX_STATUS,
  output              USER_RX_PHY_STATUS,

  input  [1:0]        GT_RXCHARISK,
  input  [15:0]       GT_RXDATA,
  input               GT_RXVALID,
  input               GT_RXELECIDLE,
  input  [ 2:0]       GT_RX_STATUS,
  input               GT_RX_PHY_STATUS,

  input               PLM_IN_L0,
  input               PLM_IN_RS,

  input               USER_CLK,
  input               RESET

);

  parameter TCQ = 1;

  parameter EIOS_DET_IDL      = 5'b00001;
  parameter EIOS_DET_NO_STR0  = 5'b00010;
  parameter EIOS_DET_STR0     = 5'b00100;
  parameter EIOS_DET_STR1     = 5'b01000;
  parameter EIOS_DET_DONE     = 5'b10000;

  parameter EIOS_COM          = 8'hBC;
  parameter EIOS_IDL          = 8'h7C;

  reg    [4:0]        reg_state_eios_det;
  wire   [4:0]        state_eios_det;

  reg                 reg_eios_detected;
  wire                eios_detected;

  reg                 reg_symbol_after_eios;
  wire                symbol_after_eios;

  parameter USER_RXVLD_IDL     = 4'b0001;
  parameter USER_RXVLD_EI      = 4'b0010;
  parameter USER_RXVLD_EI_DB0  = 4'b0100;
  parameter USER_RXVLD_EI_DB1  = 4'b1000;

  reg    [3:0]        reg_state_rxvld_ei;
  wire   [3:0]        state_rxvld_ei;

  reg    [4:0]        reg_rxvld_count;
  wire   [4:0]        rxvld_count;

  reg    [3:0]        reg_rxvld_fallback;
  wire   [3:0]        rxvld_fallback;

  reg    [1:0]        gt_rxcharisk_q;
  reg    [15:0]       gt_rxdata_q;
  reg                 gt_rxvalid_q;
  reg                 gt_rxelecidle_q;

  reg    [ 2:0]       gt_rx_status_q;
  reg                 gt_rx_phy_status_q;

  // EIOS detector

  always @(posedge USER_CLK) begin

    if (RESET) begin

      reg_eios_detected <= #TCQ 1'b0;
      reg_state_eios_det <= #TCQ EIOS_DET_IDL;
      reg_symbol_after_eios <= #TCQ 1'b0;
      gt_rxcharisk_q <= #TCQ 2'b00;
      gt_rxdata_q <= #TCQ 16'h0;
      gt_rxvalid_q <= #TCQ 1'b0;
      gt_rxelecidle_q <= #TCQ 1'b0;
      gt_rx_status_q <= #TCQ 3'b000;
      gt_rx_phy_status_q <= #TCQ 1'b0;

    end else begin

      reg_eios_detected <= #TCQ 1'b0;
      reg_symbol_after_eios <= #TCQ 1'b0;
      gt_rxcharisk_q <= #TCQ GT_RXCHARISK;
      gt_rxdata_q <= #TCQ GT_RXDATA;
      gt_rxvalid_q <= #TCQ GT_RXVALID;
      gt_rxelecidle_q <= #TCQ GT_RXELECIDLE;
      gt_rx_status_q <= #TCQ GT_RX_STATUS;
      gt_rx_phy_status_q <= #TCQ GT_RX_PHY_STATUS;

      case ( state_eios_det )

        EIOS_DET_IDL : begin

          if ((gt_rxcharisk_q[0]) && (gt_rxdata_q[7:0] == EIOS_COM) &&
              (gt_rxcharisk_q[1]) && (gt_rxdata_q[15:8] == EIOS_IDL)) begin

            reg_state_eios_det <= #TCQ EIOS_DET_NO_STR0;
            reg_eios_detected <= #TCQ 1'b1;

          end else if ((gt_rxcharisk_q[1]) && (gt_rxdata_q[15:8] == EIOS_COM))
            reg_state_eios_det <= #TCQ EIOS_DET_STR0;
          else
            reg_state_eios_det <= #TCQ EIOS_DET_IDL;

        end

        EIOS_DET_NO_STR0 : begin

          if ((gt_rxcharisk_q[0] && (gt_rxdata_q[7:0] == EIOS_IDL)) &&
              (gt_rxcharisk_q[1] && (gt_rxdata_q[15:8] == EIOS_IDL)))
            reg_state_eios_det <= #TCQ EIOS_DET_DONE;
          else
            reg_state_eios_det <= #TCQ EIOS_DET_IDL;

        end

        EIOS_DET_STR0 : begin

          if ((gt_rxcharisk_q[0] && (gt_rxdata_q[7:0] == EIOS_IDL)) &&
              (gt_rxcharisk_q[1] && (gt_rxdata_q[15:8] == EIOS_IDL))) begin

            reg_state_eios_det <= #TCQ EIOS_DET_STR1;
            reg_eios_detected <= #TCQ 1'b1;
            reg_symbol_after_eios <= #TCQ 1'b1;

          end else
            reg_state_eios_det <= #TCQ EIOS_DET_IDL;

        end

        EIOS_DET_STR1 : begin

          if ((gt_rxcharisk_q[0]) && (gt_rxdata_q[7:0] == EIOS_IDL))
            reg_state_eios_det <= #TCQ EIOS_DET_DONE;
          else
            reg_state_eios_det <= #TCQ EIOS_DET_IDL;

        end

        EIOS_DET_DONE : begin

          reg_state_eios_det <= #TCQ EIOS_DET_IDL;

        end

      endcase

    end

  end
  assign state_eios_det = reg_state_eios_det;
  assign eios_detected = reg_eios_detected;
  assign symbol_after_eios = reg_symbol_after_eios;

  // user_rxvalid generation

  always @(posedge USER_CLK) begin

    if (RESET) begin

      reg_state_rxvld_ei <= #TCQ USER_RXVLD_IDL;

    end else begin

      case ( state_rxvld_ei )

        USER_RXVLD_IDL : begin

          if (eios_detected)
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_EI;
          else
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_IDL;

        end

        USER_RXVLD_EI : begin

          if (!gt_rxvalid_q)
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_EI_DB0;
          else if (rxvld_fallback == 4'b1111)
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_IDL;
          else
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_EI;

        end

        USER_RXVLD_EI_DB0 : begin

          if (gt_rxvalid_q)
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_EI_DB1;
          else if (!PLM_IN_L0)
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_IDL;
          else
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_EI_DB0;

        end

        USER_RXVLD_EI_DB1 : begin

          if (rxvld_count > CLK_COR_MIN_LAT)
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_IDL;
          else
            reg_state_rxvld_ei <= #TCQ USER_RXVLD_EI_DB1;

        end

      endcase

    end

  end
  assign state_rxvld_ei = reg_state_rxvld_ei;

  // RxValid counter

  always @(posedge USER_CLK) begin

    if (RESET) begin

      reg_rxvld_count <= #TCQ 5'b00000;

    end else begin

      if ((gt_rxvalid_q) &&  (state_rxvld_ei == USER_RXVLD_EI_DB1))
        reg_rxvld_count <= #TCQ reg_rxvld_count + 1'b1;
      else
        reg_rxvld_count <= #TCQ 5'b00000;

    end

  end
  assign rxvld_count = reg_rxvld_count;

  // RxValid fallback

  always @(posedge USER_CLK) begin

    if (RESET) begin

      reg_rxvld_fallback <= #TCQ 4'b0000;

    end else begin

      if (state_rxvld_ei == USER_RXVLD_EI)
        reg_rxvld_fallback <= #TCQ reg_rxvld_fallback + 1'b1;
      else
        reg_rxvld_fallback <= #TCQ 4'b0000;

    end

  end
  assign rxvld_fallback = reg_rxvld_fallback;

  // Delay pipe_rx_elec_idle

  SRL16E #(.INIT(0)) rx_elec_idle_delay (.Q(USER_RXELECIDLE),
                                         .D(gt_rxelecidle_q),
                                         .CLK(USER_CLK),
                                         .CE(1'b1), .A3(1'b1),.A2(1'b1),.A1(1'b1),.A0(1'b1));


  //
  // Delay RXVALID assertion for 64 pipe cycles
  //

  parameter TS1_FILTER_IDLE       = 3'b001;
  parameter TS1_FILTER_WAITVALID  = 3'b010;
  parameter TS1_FILTER_DB         = 3'b100;

  reg [2:0] ts1_state;
  reg [2:0] next_ts1_state;
  reg       ts1_resetcount;
  reg [8:0] ts1_count;
  reg       ts1_filter_done;
  reg       next_ts1_filter_done;

  always @(*) begin

    next_ts1_state = ts1_state;
    ts1_resetcount = 0;
    next_ts1_filter_done = ts1_filter_done;

    case ( ts1_state )

      TS1_FILTER_IDLE : begin

        ts1_resetcount = 1;
        if (PLM_IN_RS) begin

          if (!ts1_filter_done)
            next_ts1_state = TS1_FILTER_WAITVALID;

        end else
          next_ts1_filter_done  = 1'b0;


      end

      TS1_FILTER_WAITVALID : begin

        ts1_resetcount = 1;
        next_ts1_filter_done = 1'b0;
        if(gt_rxvalid_q && !gt_rxelecidle_q)
          next_ts1_state = TS1_FILTER_DB;

      end

      TS1_FILTER_DB : begin

        next_ts1_filter_done = 1'b0;
        if(ts1_count[6]) begin

          next_ts1_state = TS1_FILTER_IDLE;
          next_ts1_filter_done  = 1'b1;

        end

      end

    endcase

  end

  always @(posedge USER_CLK) begin

    if (RESET) begin

      ts1_state <= #TCQ TS1_FILTER_IDLE;
      ts1_count <= #TCQ 0;
      ts1_filter_done <= #TCQ 0;

    end else begin

      ts1_state <= #TCQ next_ts1_state;
      ts1_filter_done <= #TCQ next_ts1_filter_done;

      if(ts1_resetcount)
        ts1_count <= #TCQ 0;
      else
        ts1_count <= #TCQ ts1_count + 1;

    end

  end

  assign USER_RXVALID = ((state_rxvld_ei == USER_RXVLD_IDL) && (ts1_state == TS1_FILTER_IDLE)) ? gt_rxvalid_q : 1'b0;
  assign USER_RXCHARISK[0] = USER_RXVALID ? gt_rxcharisk_q[0] : 1'b0;
  assign USER_RXCHARISK[1] = (USER_RXVALID && !symbol_after_eios) ? gt_rxcharisk_q[1] : 1'b0;
  assign USER_RXDATA = gt_rxdata_q;
  assign USER_RX_STATUS = (state_rxvld_ei == USER_RXVLD_IDL) ? gt_rx_status_q : 3'b000;
  assign USER_RX_PHY_STATUS = gt_rx_phy_status_q;

endmodule
